#include"compress_cpu.h"
#include<math.h>

void cpu_RLE(uint32* data, uint32 data_len, uint32* compressed, uint32* compressed_len, uint32* compressedSize) 
{ 
	uint32 i,j,k=0; 
	for(i=0; i<data_len; i++) { 
		j = i+1; 
		while(data[j] == data[i] && j < data_len) { 
			j++; 
		} 
		compressed[k] = data[i]; 
		compressed_len[k] = j - i; 
		k++; 
		i = j-1; 
	} 
	printf("entry num: %d\n", data_len); 
	printf("Compressed entry num: %d\n", k); 
	(*compressedSize) = k; 
} 

void cpu_RLE(uint8* data, uint32 data_len, uint8* compressed, uint32* compressed_len, uint32* compressedSize) 
{ 
	uint32 i,j,k=0; 
	for(i=0; i<data_len; i++) { 
		j = i+1; 
		while(data[j] == data[i] && j < data_len) { 
			j++; 
		} 
		compressed[k] = data[i]; 
		compressed_len[k] = j - i; 
		k++; 
		i = j-1; 
	} 
	printf("entry num: %d\n", data_len); 
	printf("Compressed entry num: %d\n", k); 
	*compressedSize = k; 
} 

void cpu_RLE_decompression(uint8* data, uint32* data_len, uint32 dataSize, uint8* outData) 
{ 
	PRINT_FUNC_NAME;
	uint32 i,j,k; 
	uint8 d; 
	uint32 l; 
	k = 0; 
	for(i=0; i<dataSize; i++) { 
		d = data[i]; 
		l = data_len[i]; 
		for(j=0; j<l; j++) {     
			outData[k++] = d;                
		} 
	} 
} 

void cpu_RLE_decompression(uint32* data, uint32* data_len, uint32 dataSize, uint32* outData) 
{ 
	PRINT_FUNC_NAME;
	uint32 i,j,k; 
	uint32 d; 
	uint32 l; 
	k = 0; 
	for(i=0; i<dataSize; i++) { 
		d = data[i]; 
		l = data_len[i]; 
		for(j=0; j<l; j++) {     
			outData[k++] = d;                
		} 
	} 
}
void cpu_delta_compress(int* dataBuffer, uint32 width, uint32 height)
{
	uint32 i,j;
	for(i=0; i<height; i++) {
		for(j=width-1; j>0; j--)
			dataBuffer[i * width + j] = dataBuffer[i*width + j] - dataBuffer[i*width +j-1];
	}
}

void cpu_delta_decompress(int* dataBuffer, uint32 width, uint32 height)
{

	PRINT_FUNC_NAME;
	uint32 i,j;
	for(i=0; i<height; i++) {
		for(j=1; j<width; j++)
			dataBuffer[i * width + j] += dataBuffer[i*width +j-1];
	}
}
unsigned int dictCompress(uint8* strBuffer, uint32* distinctStrCount, char* str)
{
	uint32 i,j;
	uint32 distinctValue = (*distinctStrCount);
	for(i=0; i<distinctValue; i++) {
		//compare
		for(j=0; j<strlen(str); j++) {
			if(strBuffer[i*MAX_STR_LEN + j] != str[j])
				break;
		}
		if(j == strlen(str))
			return i;
	}

	if(i == distinctValue){
		for(j=0; j<strlen(str); j++)
			strBuffer[distinctValue * MAX_STR_LEN + j] = str[j];
		strBuffer[distinctValue * MAX_STR_LEN + j] = '\0';
	}
	distinctValue++;
	(*distinctStrCount) = distinctValue;
	return i;
}

char* dictDecompress(uint8* strBuffer, uint32 idx, char* str)
{
	int i;
	for(i=0; i<MAX_STR_LEN; i++) {
		str[i] = strBuffer[idx*MAX_STR_LEN + i];
		if(str[i] == '\0')
			break;
	}
	return str;
}

void putBit(uint8* dest, uint64 pos, bool bit) {
	int curByte = pos/8;
	dest[curByte] <<= 1;
	dest[curByte] |= bit;
}

bool getBit(uint8* source, uint64 pos) {
	int curByte = pos/8;
	uint8 tempByte = source[curByte];
	tempByte >>= (7- (pos%8));
	bool bit = tempByte & 1;
	return bit;
}

//r = log2M
void golombEncode(uint32* source, uint8* dest, int M, uint32 dataSize, uint64* destBitSize)
{
	uint32 i,j,num,q;
	uint64 curPos = 0;
	int r = log((double)M)/log((double)2);
	for(i=0; i<dataSize; i++)
	{
		num = source[i];
		//	printf("%u, %u, %u\n", i, num, curPos);
		q = num / M;
		for (j = 0 ; j < q; j++){
			putBit(dest,curPos,true);   // write q ones
			curPos++;
		}
		putBit(dest,curPos,false);      // write one zero
		curPos++;

		int v = 1;
		for (j = 0 ; j < r; j++)
		{            
			putBit(dest, curPos, v & num );
			curPos++;
			v = v << 1;         
		}
	}
	uint64 temp = curPos;
	while(temp%8 != 0) {
		putBit(dest,temp, false);
		temp++;
	}
	(*destBitSize) = curPos;
}

void golombEncode(uint8* source, uint8* dest, int M, uint32 dataSize, uint64* destBitSize)
{
	uint32 i,j,num, q;
	uint64 curPos = 0;
	int r = log((double)M)/log((double)2);
	for(i=0; i<dataSize; i++)
	{
		num = source[i];
		q = num / M;
		for (j = 0 ; j < q; j++){
			putBit(dest,curPos,true);   // write q ones
			curPos++;
		}
		putBit(dest,curPos,false);      // write one zero
		curPos++;

		int v = 1;
		for (j = 0 ; j < r; j++)
		{            
			putBit(dest, curPos, v & num );
			curPos++;
			v = v << 1;         
		}
	}
	uint64 temp = curPos;
	while(temp%8 != 0) {
		putBit(dest,temp, false);
		temp++;
	}
	(*destBitSize) = curPos;
}

void golombDecode(uint8* source, uint32* dest, int M, uint64 sourceBitSize)
{
	int q = 0;
	uint32 nr = 0;
	uint64 curPos = 0;
	int a, r;
	r = log((double)M)/log((double)2);
	uint32 destSize = 0;
	while (curPos < sourceBitSize)
	{
		nr = 0;
		q = 0;
		while (getBit(source,curPos++)){ 
			q++;	                      // potentially dangerous with malformed files.
		}
		for (a = 0; a < r && curPos < sourceBitSize; a++){         // read out the sequential log2(M) bits
			if (getBit(source,curPos++))
				nr += 1 << a;
		}
		nr += q*M;                          // add the bits and the multiple of M
		dest[(destSize)++] = nr;               // write out the value
	}
}

void golombDecode(uint8* source, uint8* dest, int M, uint64 sourceBitSize)
{
	int q = 0;
	uint32 nr = 0;
	uint64 curPos = 0;
	int a, r;
	r = log((double)M)/log((double)2);
	uint32 destSize = 0;
	while (curPos < sourceBitSize)
	{
		nr = 0;
		q = 0;
		while (getBit(source,curPos++)){ 
			q++;	                      // potentially dangerous with malformed files.
		}
		for (a = 0; a < r && curPos < sourceBitSize; a++)         // read out the sequential log2(M) bits
			if (getBit(source,curPos++))
				nr += 1 << a;
		nr += q*M;                          // add the bits and the multiple of M
		dest[(destSize)++] = (uint8)nr;               // write out the value
	}
}

int countDistinct(uint8* data, int len) {
	int dist = 0;
	uint8 map[256];
	int i;
	memset(map, 0, 256);
	for(i=0; i<len; i++) {
		map[data[i]] = 1;
	}
	for(i=0; i<256; i++)
		dist += map[i];
	return dist;
}

double getAverageRunLen(uint8* data, int len) {
	int runLen = 0, num = 0;
	int i,j;
	for(i=0; i<len; i++) {
		j = i + 1;
		while( j < len && data[j] == data[i]) {
			j++;
			runLen++;
		}
		num++;
		i = j - 1;
	}
	return (double)runLen / (double)num;
}

double getAverageRunLen(uint32* data, uint32 len) {
	uint32 runLen = 0, num = 0;
	uint32 i,j;
	for(i=0; i<len; i++) {
		j = i + 1;
		while( j < len && data[j] == data[i]) {
			j++;
			runLen++;
		}
		num++;
		i = j - 1;
	}
	return (double)runLen / (double)num;
}

	uint8 smaller(uint8 a, uint8 b) {
		if(a > b)
			return b;
		return a;
	}

	uint8 abs_delta(uint8 a, uint8 b) {
		if(a > b)
			return a-b;
		return b-a;
	}

void cpu_binaryEncoding_2bit(uint8* source, uint8* dest, uint32 sourceSize, uint32* destSize) {
	uint32 i,j=0;
	uint8 temp=0;
	for(i=0; i<sourceSize; i++) {
		temp <<= 2;
		temp |= source[i];
		j++;
		if(j == 4) {
			dest[i/4] = temp;
			j = 0;
			temp = 0;
		}
	}
	if(j != 0) {
		while(j < 4) {
			temp <<= 2;
			j++;
		}
		dest[i/4] = temp;
	}
	(*destSize) = ceil((double)i/4.0);
}

void cpu_binaryDecoding_2bit(uint8* source, uint8* dest, uint32 sourceSize)
{
	uint32 i;
	uint8 temp;
	int j=0;
	for(i=0; i<sourceSize; i++) {
		temp = source[i];
		for(j=3; j>=0; j--) {
			dest[i*4+j] = temp & 0x3;
			temp >>= 2;
		}
	}
}
